﻿using System;
using Demo.Framework.Business;
using Demo.Framework.Business.Debug;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace Demo.Framework
{
    class CModel : IDisposable
    {
        #region fields

        private Effect _ambientLightEffect;

        private readonly GraphicsDevice _graphics;
        private Model _model;
        private readonly Vector3 _position;
        private readonly CBoundingBox _bBox;
        private Vector3 _rotation;
        private readonly Vector3 _scale;
        private Matrix _world;
        private BasicEffect _basicEffect;

        #endregion

        #region props

        public Model Model
        {
            get { return _model; }
        }

        public Vector3 Position
        {
            get { return _position; }
        }

        public Vector3 Scale
        {
            get { return _scale; }
        }

        #endregion

        #region ctor

        public CModel(GraphicsDevice graphics)
        {
            _graphics = graphics;
            _bBox = new CBoundingBox(_graphics);
        }

        public CModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, GraphicsDevice graphicsDevice)
            : this(graphicsDevice)
        {
            _model = model;
            _position = position;
            _rotation = rotation;
            _scale = scale;
        }

        #endregion

        public void SetModelEffect(Effect effect, bool copyEffect)
        {
            foreach (ModelMesh mesh in Model.Meshes)
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    Effect toSet = effect;

                    // Copy the effect if necessary
                    if (copyEffect)
                        toSet = effect.Clone();

                    MeshTag tag = ((MeshTag)part.Tag);

                    // If this ModelMeshPart has a texture, set it to the effect
                    if (tag.Texture != null)
                    {
                        setEffectParameter(toSet, "BasicTexture", tag.Texture);
                        setEffectParameter(toSet, "TextureEnabled", true);
                    }
                    else
                        setEffectParameter(toSet, "TextureEnabled", false);

                    // Set our remaining parameters to the effect
                    setEffectParameter(toSet, "DiffuseColor", tag.Color);
                    setEffectParameter(toSet, "SpecularPower", tag.SpecularPower);

                    part.Effect = toSet;
                }
        }

        void setEffectParameter(Effect effect, string paramName, object val)
        {
            if (effect.Parameters[paramName] == null)
                return;

            if (val is Vector3)
                effect.Parameters[paramName].SetValue((Vector3)val);
            else if (val is bool)
                effect.Parameters[paramName].SetValue((bool)val);
            else if (val is Matrix)
                effect.Parameters[paramName].SetValue((Matrix)val);
            else if (val is Texture2D)
                effect.Parameters[paramName].SetValue((Texture2D)val);
        }

        public void LoadContent(string modelAssetName, ContentManager content)
        {
            _model = content.Load<Model>(modelAssetName);
            _bBox.CreateBBox(Model, Matrix.CreateTranslation(new Vector3(0,0,0)));

            _ambientLightEffect = content.Load<Effect>("Effects//Specular");
            _basicEffect = new BasicEffect(_graphics);
        }

        public void Update()
        {
            //_rotation.Y += 0.01f;
            _world = Matrix.CreateRotationX(_rotation.X) * Matrix.CreateRotationY(_rotation.Y) * Matrix.CreateRotationZ(_rotation.Z)*Matrix.CreateTranslation(300f, 0.0f, -300.0f);
            
        }

        public void Draw(bool player)
        {
            var proj = GameEngine.Instance.Camera.Projection;
            var view = GameEngine.Instance.Camera.View;
            var world = Matrix.Identity;//Matrix.CreateRotationX(-(float)(90 * (Math.PI / 180))) * _world;
            //var viewVector = GameEngine.Instance.Camera.ViewVector;
            var worldInverseTransposeMatrix = Matrix.Transpose(Matrix.Invert(world));

            Effect curentEffect;
            if (DebugSettings.EnableEffects)
            {
                curentEffect = _ambientLightEffect;
                _ambientLightEffect.Parameters["WorldInverseTranspose"].SetValue(worldInverseTransposeMatrix);
                _ambientLightEffect.Parameters["World"].SetValue(world);
                _ambientLightEffect.Parameters["View"].SetValue(view);
                _ambientLightEffect.Parameters["Projection"].SetValue(proj);
                _ambientLightEffect.Parameters["AmbientColor"].SetValue(Color.BlanchedAlmond.ToVector4());
                _ambientLightEffect.Parameters["AmbientIntensity"].SetValue(0.25f);
                //_ambientLightEffect.Parameters["ViewVector"].SetValue(viewVector);
                _ambientLightEffect.Parameters["DiffuseLightDirection"].SetValue(new Vector3(1.2f,1,1));
                _ambientLightEffect.Parameters["DiffuseIntensity"].SetValue(0.6f);
                _ambientLightEffect.Parameters["Shininess"].SetValue(500);
                _ambientLightEffect.Parameters["SpecularIntensity"].SetValue(0.7f);
            }
            else
            {
                curentEffect = _basicEffect;
                ((BasicEffect)curentEffect).EnableDefaultLighting();
                ((BasicEffect)curentEffect).World = world;
                ((BasicEffect)curentEffect).Projection = proj;
                ((BasicEffect)curentEffect).View = view;
            }

            Draw(curentEffect);
            
            //_bBox.Draw();
        }

        private void Draw(Effect effect)
        {
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect = effect;
                }
                mesh.Draw();
            }
        }



        #region Implementation of IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            _model = null;
        }

        #endregion
    }
}
